home *** CD-ROM | disk | FTP | other *** search
/ Alles Voor Internet / Tout Pour Internet / alles voor internet.iso / MacInternet™ / Telnet / Terminal 2.2 / Terminal Folder / MiniBBS.s < prev    next >
Text File  |  1990-12-31  |  16KB  |  607 lines

  1. /*
  2.     Terminal 2.0
  3.     "MiniBBS.s"
  4.  
  5. This script sets up a small BBS. To try it out connect another computer
  6. directly to your Macintosh (an ImageWriter II cable can be used to connect
  7. two Macintosh computers, or the same Macintosh computer between the modem
  8. and the printer port) and set the variable "modem" to 0.
  9.  
  10. In real operation you would set the variable "modem" to 1, configure your
  11. modem to auto-answer and wait for others to call you. Configure the modem
  12. so that a drop of the DTR line is recognized as a command to hang up. This
  13. way to hang up is more reliable than sending the "+++" escape sequence,
  14. followed by the command "ATH0", to the modem.
  15.  
  16. Note: the script works fine with my modems. My old modem had problems to
  17. recognize if the other end had hung up. If the other end hanged up the
  18. carrier detect stayed on, my modem did not hang up and continually sent me
  19. a lot of garbage. If the X/Y-Modem file transfer detects errors it would
  20. wait for the line to clear before giving up. But because the modem
  21. continued to send garbage there was no way to get out of the file transfer.
  22. This has been corrected since "Terminal 1.9" and now the X/Y-Modem file
  23. transfer will not wait endlessly for the line to clear but for one minute
  24. and then gives up. This makes unattended operation more reliable, as it
  25. should be for a BBS!
  26.  
  27. When the BBS receives the string "RING\r" it waits for ten seconds to
  28. receive the string "CONNECT\r", "CONNECT 1200\r" or "CONNECT 2400\r" (this
  29. indicates that the modem has answered an incoming call at 300, 1200 or 2400
  30. Baud) and then it prompts for a password after adapting its speed. Once the
  31. correct password is given the BSS will prompt for commands. The password is
  32. not echoed to the caller, nor is it echoed to the screen. The password to
  33. use is hard-coded into the script. There is a retry limit of 3 on the
  34. password, then the BBS will hang up. The BBS is command line oriented. You
  35. enter commands on a line using a command name followed by a parameter if
  36. needed, then press "Return".
  37.  
  38. There are some timeout values hard-coded into the script. If waiting for
  39. commands the BBS will timeout and log you out when no command is received
  40. after some time. If you make 10 consecutive command errors you are logged
  41. out. This measure was necessary, because my (old) modem did send me a
  42. continous stream of garbage when the other side hanged up (see note above)
  43. and this blocked the BBS. There is also a login timeout. If you have been
  44. logged in for some time you are logged out automatically. While promptimg
  45. for a new command the state of the DCD (data carrier detect) is checked. If
  46. carrier is lost the BBS logs out.
  47.  
  48. The upload and download commands will not use MacBinary format, but it
  49. should be easy to change the script so that they do. Any file uploaded is
  50. stored on the disk as received. It may be a file from another computer
  51. system, not necessarily a Macintosh. If you want to make Macintosh files
  52. available to your subscribers you must first convert them to MacBinary
  53. format using the "Make MacBinary..." menu item of "Terminal" before storing
  54. them in the down- and upload folder. An upload cannot use the name of an
  55. existing file. There is no way to delete files. The folder on the disk
  56. where all file operations take place is the folder that can be set up using
  57. the "Binary file transfer" menu option. There is no way to switch to
  58. another folder thru BBS commands.
  59.  
  60. This script is a good example of the "Terminal" script language. It shows
  61. most constructs available in this subset of the C language and most of the
  62. built-in intrinsic functions. You should especially look for and understand
  63. the recognition of commands, where an array of function pointers is used,
  64. some pointer arithmetic is done, and functions are called indirectly thru
  65. pointers.
  66.  
  67. Note that the possibility to cancel scripts depends on the goodwill of the
  68. script itself. It should test the return codes of the intrinsic functions
  69. it calls and recognize the cancel return code (2) and then exit gracefully.
  70. */
  71.  
  72. /* ----- Configuration ------------------------------------------------- */
  73.  
  74. int modem = 1;                /* 0: direct connect, 1: auto-answer modem */
  75. int carrier = 1;            /* 0: ignore DCD, 1: check DCD */
  76. int LIMIT0 = 60*60;            /* Timeout for password (ticks) */
  77. int LIMIT1 = 3*60*60;        /* Timeout for command prompt (ticks) */
  78. int LIMIT2 = 7200;            /* Timeout for login session (seconds) */
  79. char PASSWORD[] =            /* Login password */
  80.     "LET ME IN";
  81.  
  82. /* ----- Global data --------------------------------------------------- */
  83.  
  84. char Version[] = "2.0";
  85.  
  86. int TIMEOUT = 1;
  87. int CANCEL = 2;        /* Script canceled by menu command */
  88. int ABORT = 3;        /* Abort by (2 consecutive) control-X characters */
  89. int ILLEGAL = 4;    /* Wrong password */
  90. int NOCARRIER = 5;    /* No data carrier (DCD off) */
  91. int QUIT = 6;
  92.  
  93. char M_timeout[]    = "\r*** Timeout\r";
  94. char M_error[]        = "*** Error\r";
  95. char M_prompt[]        = "\r> ";
  96. char M_fileErr[]     = "\r*** File error %i\r";
  97. char M_catalog[]    = " BYTES    LAST MODIFIED    NAME\r";
  98. char M_file[]        = "%6i  %s  %s\r";
  99. char M_abort[]        = "Press control-X twice to abort\r";
  100.  
  101. char *Days[] = {            /* Weekday names */
  102.     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  103. };
  104.  
  105. char *Month[] = {            /* Month names */
  106.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  107.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  108. };
  109.  
  110. int IGNORE = -1;
  111.  
  112. char Line[256];                /* Command line */
  113. char *Mark;                    /* Running pointer into command line */
  114. int Timer;                    /* Login timer */
  115. int Errors;                    /* Error counter */
  116.  
  117. /* ----- Verify file name ---------------------------------------------- */
  118.  
  119. /* In order to make sure nobody can access any folder other than the down-
  120. and upload folder, we must check for ':' in file names. Imagine the
  121. following commands, that some hacker might send:
  122.     TYPE ::Terminal Folder:miniBBS.s
  123.     DOWNLOAD HD:Confidential:Very confidential file */
  124.  
  125. checkname (char *name)
  126. {
  127.     char c;
  128.  
  129.     if (*name == '.' || *name == 0)
  130.         return -37;        /* Illegal file name */
  131.     while (c = *name++)
  132.         if (c == ':')
  133.             return -37;    /* Illegal file name */
  134.     return 0;            /* File name ok */
  135. }
  136.  
  137. /* ----- Convert date and time to string ------------------------------- */
  138.  
  139. sec2str (int sec, char *s)
  140. {
  141.     int year, month, day, hour, minute, second, weekday;
  142.  
  143.     date(sec, &year, &month, &day, &hour, &minute, &second, &weekday);
  144.     format(s, "%02i-%s-%04i %02i:%02i:%02i %s",
  145.         day, Month[month-1], year, hour, minute, second, Days[weekday-1]);
  146. }
  147.  
  148. /* ----- Type date and time -------------------------------------------- */
  149.  
  150. clock (int sec)
  151. {
  152.     char s[30];
  153.  
  154.     sec2str(sec, s);
  155.     type("%s\r", s);
  156. }
  157.  
  158. /* ----- Type modem intialisation string ------------------------------- */
  159.  
  160. /* This is for my Abaton InterFax 24/96 modem:
  161.     E0        Commands are not echoed to the screen
  162.     X1        OK, ERROR, CONNECT speed, RING, NO CARRIER responses
  163.     S2=255    Modem does not recognize escape command
  164.     S0=2    Auto-answer, wait for 2 RINGs
  165.     S7=60    Wait time for carrier is 60 seconds
  166.     S10=14    If carrier lost for more than 1.4 seconds hang up
  167.     S9=12    Carrier must be present for 1.2 seconds to be recognized
  168.     &C1        DCD shows that a carrier is present only when one is
  169.     !C1        The DCD is output on pin 5 (CTS), pin 8 not used */
  170.  
  171. setmodem ()
  172. {
  173.     type("ATE0X1S2=255S0=2S7=60S10=14S9=12");
  174.     if (carrier)
  175.         type("&C1!C1\r");
  176.     else
  177.         type("&C0!C0\r");
  178. }
  179.  
  180. /* ----- Check for data carrier detect --------------------------------- */
  181.  
  182. dcd ()
  183. {
  184.     if (carrier)
  185.         return getcts();    /* DCD has been moved to pin 5 (CTS) */
  186.     else
  187.         return 1;
  188. }
  189.  
  190. /* ----- Main function ------------------------------------------------- */
  191.  
  192. main ()
  193. {
  194.     int flg = 0, e = 0, i;
  195.  
  196.     display("MiniBBS %s activated (%i bytes free)\r", Version, stack());
  197.     setup(
  198.         3,        /* 2400 baud */
  199.         1,        /* 8 data */
  200.         0,        /* no parity */
  201.         0,        /* 1 stop */
  202.         IGNORE,    /* port: no change */
  203.         IGNORE,    /* DTR: no change */
  204.         0);        /* Handshake: none */
  205.     terminal(
  206.         0,        /* Local echo off */
  207.         0,        /* Remote echo off */
  208.         0,        /* AutoLF off */
  209.         1);        /* Save & display on */
  210.     protocol(
  211.         0,        /* no MacBinary */
  212.         0,        /* no QuickB */
  213.         0,        /* no ZModem */
  214.         0);        /* no Zmodem autoreceive */
  215.     xyparms(
  216.         1,        /* crc */
  217.         1,        /* 1K */
  218.         0,        /* no batch */
  219.         600);    /* timeout = 10 sec */
  220.     text(
  221.         "",        /* No prompt string */
  222.         0,        /* No line delay */
  223.         0);        /* No character delay */
  224.     macrol("MiniBBS.m");    /* Load macros file */
  225.  
  226.     while (!e && flg != CANCEL) {
  227.         lecho(0);    /* Local echo off */
  228.         recho(0);    /* Remote echo off */
  229.  
  230.         if (modem) {
  231.             setdtr(0);    /* Negate DTR: modem hangs up */
  232.             pause(60);
  233.             setdtr(1);    /* Assert DTR: now back in command mode */
  234.  
  235.             /* Try (up to 5 times) to reset and setup modem */
  236.  
  237.             for (i = 0; i < 5; ++i) {
  238.                 type("ATZ\r");                            /* Reset modem */
  239.                 if (!(e = prompt("OK\r", 120))) {
  240.                     pause(30);
  241.                     setmodem();
  242.                     if (!(e = prompt("OK\r", 120))) {
  243.                         lecho(1);                        /* Local echo on */
  244.                         break;
  245.                     }
  246.                 }
  247.             }
  248.             if (e)
  249.                 break;    /* Could not setup modem */
  250.  
  251.             /* Wait until modem says "RING" (or script is canceled) */
  252.  
  253.             if (!(flg = prompt("RING\r", 0))) {    /* No timeout */
  254.  
  255.                 /* See what speed (ignore additional "RING"s) */
  256.  
  257.                 i = IGNORE;
  258.                 while (i == IGNORE) {
  259.                     char *s;
  260.                     if (flg = nextline(Line, 600))
  261.                         break;
  262.                     s = Strip(Line);
  263.                     if (!strcmp(s, "CONNECT 2400"))
  264.                         i = 3;    /* 2400 Baud */
  265.                     else if (!strcmp(s, "CONNECT 1200"))
  266.                         i = 2;    /* 1200 Baud */
  267.                     else if (!strcmp(s, "CONNECT"))
  268.                         i = 0;    /* 300 Baud */
  269.                 }
  270.                 if (!flg) {
  271.  
  272.                     /* Set speed and enter command loop */
  273.  
  274.                     setup(i, IGNORE, IGNORE, IGNORE, IGNORE, IGNORE);
  275.                     pause(300);    /* Give modem a chance */
  276.                     flg = loop();
  277.                 }
  278.             }
  279.  
  280.         } else {        /* Direct connection (wait for CR) */
  281.             if (!(flg = prompt("\r", 0)))
  282.                 flg = loop();
  283.         }
  284.     }
  285.     if (modem) {
  286.         setdtr(0);    /* Negate DTR: modem hangs up */
  287.         pause(60);
  288.     }
  289.     setdtr(1);    /* Assert DTR: now back in command mode */
  290.     if (e == TIMEOUT)
  291.         display("Modem problem\r");
  292.     display("End of MiniBBS mode\r");
  293.     return 1;    /* Restore previous settings */
  294. }
  295.  
  296. /* ----- Strip leading control characters (like LFs) ------------------- */
  297.  
  298. Strip(char *line)
  299. {
  300.     while (*line && (*line < 0x20 || *line > 0x7E))
  301.         ++line;
  302.     return line;
  303. }
  304.  
  305. /* ----- Login session ------------------------------------------------- */
  306.  
  307. loop ()
  308. {
  309.     int flg, retry;
  310.     char s[30];
  311.  
  312.     /* Prompt for password (3 retries), wait for it, check it */
  313.  
  314.     retry = 3;
  315.     while(retry--) {
  316.         sec2str(time(), s);
  317.         type("\r%s  Password [%s]: ", s, PASSWORD);
  318.         save(0);                            /* Save & display off */
  319.         flg = nextline(Line, LIMIT0);
  320.         save(1);                            /* Save & display on */
  321.         if (flg == CANCEL)
  322.             return flg;
  323.         if (!dcd())
  324.             return NOCARRIER;
  325.         if (flg == TIMEOUT)
  326.             type(M_timeout);
  327.         if (flg == 0) {
  328.             if (!strcmp(Strip(Line), PASSWORD))
  329.                 break;                        /* Correct password */
  330.             flg = ILLEGAL;                    /* Wrong password */
  331.         }
  332.     }
  333.     if (flg) {
  334.         type("\rBye bye...\r");
  335.         return flg;                            /* Wrong password */
  336.     }
  337.  
  338.     /* Password was correct, now logged in */
  339.  
  340.     macrox(0, 0);                            /* Type date & time and ... */
  341.     clock(Timer = time());                    /*     start login timer */
  342.     recho(1);                                /* Remote echo on */
  343.  
  344.     /* Prompt and wait for commands (with timeout) */
  345.  
  346.     Errors = 0;
  347.     flg = 0;
  348.     while (!flg) {
  349.         type(M_prompt);
  350.         if ((flg = nextline(Line, LIMIT1)) == CANCEL)
  351.             return flg;                        /* Cancel */
  352.         if (!dcd())
  353.             return NOCARRIER;
  354.         if (!flg) {
  355.             flg = command();                /* Execute command */
  356.             if ((time() - Timer) > LIMIT2)
  357.                 flg = TIMEOUT;                /* Set timeout */
  358.         }
  359.         if (Errors > 10)
  360.             flg = TIMEOUT;
  361.     }
  362.  
  363.     /* Log out (command has set "flg" to non-zero value) */
  364.  
  365.     recho(0);                        /* Remote echo off */
  366.     if (flg == TIMEOUT)
  367.         type(M_timeout);
  368.     macrox(1, 0);
  369.     datetime();
  370.     return flg;                        /* Timeout, cancel or quit */
  371. }
  372.  
  373. /* ----- Execute command line ------------------------------------------ */
  374.  
  375. command ()
  376. {
  377.     char name[256];            /* Command name extracted from command line */
  378.     char **p = Commands;    /* Pointer into command names array */
  379.  
  380.     Mark = Strip(Line);        /* Reset command line pointer */
  381.     if (getnext(name)) {    /* Get first word from command line */
  382.         while (*p) {        /* Search thru valid command names */
  383.             if (!strcmp(name, *p)) {
  384.                 Errors = 0;
  385.                 return (Pointers[p - Commands])();    /* Execute command */
  386.             }
  387.             ++p;
  388.         }
  389.     }
  390.     type(M_error);            /* Command not recognized */
  391.     ++Errors;
  392.     return 0;
  393. }
  394.  
  395. /* ----- Get next word from command line ------------------------------- */
  396.  
  397. getnext (char *s)
  398. {
  399.     int n;
  400.  
  401.     n = 0;
  402.     while (*Mark == ' ' && n < 255) {    /* Skip leading spaces */
  403.         ++Mark;
  404.         ++n;
  405.     }
  406.     n = 0;
  407.     while (*Mark && *Mark != ' ' && n < 255) {    /* Copy word */
  408.         *s++ = *Mark++;
  409.         ++n;
  410.     }
  411.     *s = '\0';        /* \0 as end of string */
  412.     if (*Mark)
  413.         ++Mark;        /* Skip blank, now Mark -> name */
  414.     return n;        /* Return string length */
  415. }
  416.  
  417. /* ----- Help command -------------------------------------------------- */
  418.  
  419. help ()
  420. {
  421.     char **p = Helps, **q = Commands;
  422.  
  423.     while (*p && *q)
  424.         type("%-5s %s\r", *q++, *p++);
  425.     return 0;        /* Don't quit */
  426. }
  427.  
  428. /* ----- Info command -------------------------------------------------- */
  429.  
  430. info ()
  431. {
  432.     type("MiniBBS version %s\r", Version);
  433.     macrox(2, 0);
  434.     return 0;        /* Dont't quit */
  435. }
  436.  
  437. /* ----- Quit command -------------------------------------------------- */
  438.  
  439. quit ()
  440. {
  441.     return QUIT;    /* Now quit */
  442. }
  443.  
  444. /* ----- Type date & time and login time ------------------------------- */
  445.  
  446. datetime ()
  447. {
  448.     int t = time();
  449.  
  450.     clock(t);
  451.     t = t - Timer;
  452.     type("Login time is %i'%02i\" of %i'%02i\" maximum\r",
  453.         t / 60, t % 60, LIMIT2 / 60, LIMIT2 % 60);
  454.     return 0;        /* Don't quit */
  455. }
  456.  
  457. /* ----- Type file directory ------------------------------------------- */
  458.  
  459. directory ()
  460. {
  461.     int i;
  462.     char name[32];
  463.     int data, rsrc, creat, modif, ftype;
  464.     char md[32];
  465.  
  466.     if (*Mark) {    /* File name specified */
  467.         if (!(i = checkname(Mark)))
  468.             i = catalog(0, Mark, &ftype, &data, &rsrc, &creat, &modif);
  469.         if (i)
  470.             type(M_fileErr, i);
  471.         else {
  472.             type(M_catalog);
  473.             sec2str(modif, md);
  474.             md[17] = '\0';    /* Truncate string */
  475.             type(M_file, data, md, Mark);
  476.         }
  477.     } else {        /* No file name specified */
  478.         type(M_catalog);
  479.         for (i = 1; ; ++i) {
  480.             if (catalog(i, name, &ftype, &data, &rsrc, &creat, &modif))
  481.                 break;
  482.             sec2str(modif, md);
  483.             md[17] = '\0';    /* Truncate string */
  484.             if (type(M_file, data, md, name)) {
  485.                 ++i;
  486.                 break;
  487.             }
  488.         }
  489.         type("%i files listed\r", i - 1);
  490.     }
  491.     return 0;        /* Don't quit */
  492. }
  493.  
  494. /* ----- Type TEXT file ------------------------------------------------ */
  495.  
  496. typefile ()
  497. {
  498.     int err;
  499.  
  500.     if (!(err = checkname(Mark))) {
  501.         type(M_abort);
  502.         err = send(Mark);
  503.     }
  504.     if (err)
  505.         type(M_fileErr, err);
  506.     return 0;        /* Don't quit */
  507. }
  508.  
  509. /* ----- Type macro text ----------------------------------------------- */
  510.  
  511. typemacro ()
  512. {
  513.     int i;
  514.     char name[50];
  515.  
  516.     if (*Mark >= '0' && *Mark <= '9') {
  517.         type(M_abort);
  518.         macrox(val(Mark), 0);
  519.     } else
  520.         for (i = 0; i < 10; ++i) {
  521.             macrox(i, 2, name);
  522.             if (*name)
  523.                 type("[%i] %s\r", i, name);
  524.         }
  525.     return 0;        /* Don't quit */
  526. }
  527.  
  528. /* ----- Download file (X-Modem) --------------------------------------- */
  529.  
  530. downloadfile ()
  531. {
  532.     int err;
  533.     int ftype, data, rsrc, creat, modif;
  534.  
  535.     if (!(err = checkname(Mark))) {
  536.         /* First see if file exists */
  537.         if (!(err = catalog(0,Mark,&ftype,&data,&rsrc,&creat,&modif))) {
  538.             type("Please start download. %s", M_abort);
  539.             err = upload(Mark, 0, 0);
  540.         }
  541.     }
  542.     if (err)
  543.         type(M_fileErr, err);
  544.     return 0;        /* Don't quit */
  545. }
  546.  
  547. /* ----- Upload file (X-Modem) ----------------------------------------- */
  548.  
  549. uploadfile ()
  550. {
  551.     int err;
  552.     int ftype, data, rsrc, creat, modif;
  553.  
  554.     if (!(err = checkname(Mark))) {
  555.         if (!(err = catalog(0,Mark,&ftype,&data,&rsrc,&creat,&modif)))
  556.             type("*** File <%s> already exists\r", Mark);
  557.         else {
  558.             type("Please start upload. %s", M_abort);
  559.             err = download(Mark, 0, 0);
  560.         }
  561.     }
  562.     if (err)
  563.         type(M_fileErr, err);
  564.     return 0;        /* Don't quit */
  565. }
  566.  
  567. /* ----- Command info -------------------------------------------------- */
  568.  
  569. char *Commands[] = {        /* Command keywords */
  570.     "?",
  571.     "DIR",
  572.     "DOWN",
  573.     "INFO",
  574.     "MACRO",
  575.     "QUIT",
  576.     "TIME",
  577.     "TYPE",
  578.     "UP",
  579.     0
  580. };
  581.  
  582. char *Helps[] = {            /* Help text for each command */
  583.     "         Display this help text",
  584.     "[name]   Directory of file[s]",
  585.     " name    Download file (X-Modem CRC 1K)",
  586.     "         Display system information",
  587.     "[number] Type macro text / list macros",
  588.     "         Logout of the system",
  589.     "         Show date and time",
  590.     " name    Type text file",
  591.     " name    Upload file (X-Modem CRC 1K)",
  592.     0
  593. };
  594.  
  595. int Pointers[] = {            /* Pointers to command functions */
  596.     help,
  597.     directory,
  598.     downloadfile,
  599.     info,
  600.     typemacro,
  601.     quit,
  602.     datetime,
  603.     typefile,
  604.     uploadfile,
  605.     0
  606. };
  607.